package com.example.ljdemo.infrastructure.util;

import com.example.ljdemo.infrastructure.responce.BizException;
import com.example.ljdemo.infrastructure.responce.ErrorCode;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

/**
 * 加密算法
 *
 * @author shishi.qu
 */
public class EncryptUtils {
    private static final String PASSWORD_SALARY_TRANSFORM = "AES/CBC/PKCS5Padding";
    private static final String PASSWORD_IV_PARAM = "0001110000000011";
    private static final String SALARY_IV_PARAM = "Rq10Lka6348C8qP8";
    private static final String SALARY_SECRET_KEY = "ic7419R1f0dU89Z92v2G97Le3235lxyD";
    private static final String MYSQL_TRANSFORM = "AES";
    private static final String MYSQL_RAW_SECRET_KEY = "$*^@!#";
    private static final SecretKeySpec MYSQL_SECRET_KEY;

    static {
        MYSQL_SECRET_KEY = generateMySQLAESKey();
    }

    private static final char[] chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v'
            , 'w', 'x', 'y', 'z', '9', '8', '7', '6', '5', '4', '3', '2', '1', '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'
            , 'W', 'X', 'Y', 'Z'};

    public static String password(String content, String key) {
        try {
            byte[] raw = key.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance(PASSWORD_SALARY_TRANSFORM);
            IvParameterSpec iv = new IvParameterSpec(PASSWORD_IV_PARAM.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
            return Hex.encodeHexString(encrypted, false);//默认返回小写,false 返回大写
        } catch (BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
            throw new BizException(ErrorCode.ENCRYPT_ERROR);
        }
    }

    public static String salary(String content) {
        try {
            byte[] raw = SALARY_SECRET_KEY.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance(PASSWORD_SALARY_TRANSFORM);
            IvParameterSpec iv = new IvParameterSpec(SALARY_IV_PARAM.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
            return Hex.encodeHexString(encrypted);//默认返回小写
        } catch (BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
            throw new BizException(ErrorCode.ENCRYPT_ERROR);
        }
    }

    public static String mysql(String content) {
        try {
            final Cipher encryptCipher = Cipher.getInstance(MYSQL_TRANSFORM);
            encryptCipher.init(Cipher.ENCRYPT_MODE, MYSQL_SECRET_KEY);
            byte[] encrypted = encryptCipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
            return Hex.encodeHexString(encrypted, false);//默认返回小写
        } catch (BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException e) {
            throw new BizException(ErrorCode.ENCRYPT_ERROR);
        }
    }

    public static String decryptMysql(String content) {
        try {
            final Cipher encryptCipher = Cipher.getInstance(MYSQL_TRANSFORM);
            encryptCipher.init(Cipher.DECRYPT_MODE, MYSQL_SECRET_KEY);
            byte[] decrypted = encryptCipher.doFinal(Hex.decodeHex(content));
            return new String(decrypted);
        } catch (BadPaddingException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | DecoderException e) {
            throw new BizException(ErrorCode.ENCRYPT_ERROR);
        }
    }


    /**
     * 随机生成AES密钥
     */
    public static String generatorKeyForAES() {
        StringBuilder buffer = new StringBuilder();
        Random random = new Random();
        int length = chars.length;
        for (int i = 0; i <= 15; i++) {
            buffer.append(chars[random.nextInt(length)]);
        }
        return buffer.toString();
    }

    /**
     * 对字符串md5加密
     *
     * @param str 待加密的字符串
     * @return encrypted text
     */
    public static String withMD5(String str) {
        try {
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update(str.getBytes());
            // digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
            // BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
            return new BigInteger(1, md.digest()).toString(16);
        } catch (Exception e) {
            throw new BizException(ErrorCode.ENCRYPT_ERROR);
        }
    }

    /**
     * @return final key
     */
    public static SecretKeySpec generateMySQLAESKey() {
        final byte[] finalKey = new byte[16];
        int i = 0;
        for (byte b : MYSQL_RAW_SECRET_KEY.getBytes(StandardCharsets.UTF_8))
            finalKey[i++ % 16] ^= b;
        return new SecretKeySpec(finalKey, "AES");
    }
}
